package com.cskaoyan.javase.thread._5pc._2demo.v2;

/**
 * 测试生产者消费者模型
 * 我们需要模拟实现：
 * 生产者生产手机 --> 存入仓库 --> 消费者从仓库消费手机...
 *
 * 思考:
 * 这里用的是同步代码块,同步代码块的锁比较灵活,它可以跨类指定锁对象
 * 但是如果用同步方法,锁对象一定是this,所以对于仓库中手机的生产和消费的方法都必须处在一个类中
 * (因为只有处在一个类中,this才是一样的)
 *
 * @since 11:37
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Test {
    public static void main(String[] args) {
        // 创建仓库对象,仓库对象是被生产者和消费者共享的
        Warehouse warehouse = new Warehouse();
        // 创建生产者和消费者线程
        new Thread(new Producer(warehouse), "生产者1号").start();
        new Thread(new Producer(warehouse), "生产者2号").start();
        new Thread(new Consumer(warehouse), "消费者1号").start();
        new Thread(new Consumer(warehouse), "消费者2号").start();
    }
}

// 生产者模型
class Producer implements Runnable {
    // 持有仓库对象的引用作为成员变量,方便存入手机
    private final Warehouse warehouse;

    private static Phone[] phones = {
            new Phone("IPhone12", 4500),
            new Phone("华为P50", 5000),
            new Phone("小米13", 6000),
            new Phone("OPPO Reno ACE", 4000)
    };

    public Producer(Warehouse warehouse) {
        this.warehouse = warehouse;
    }

    private Phone producePhone() {
        return phones[(int) (Math.random() * phones.length)];
    }

    @Override
    public void run() {
        synchronized (warehouse) {
            while (true) {
                // 1.判断仓库有没有手机
                if (warehouse.isEmpty()) {
                    // 3.如果没有手机,生产一个手机,放入仓库,notify
                    Phone phone = producePhone();
                    warehouse.pullPhone(phone);
                    warehouse.notifyAll();
                } else {
                    // 2.如果有手机,wait
                    try {
                        warehouse.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

// 消费者模型
class Consumer implements Runnable {
    // 持有仓库对象的引用作为成员变量,方便存入手机
    private final Warehouse warehouse;

    public Consumer(Warehouse warehouse) {
        this.warehouse = warehouse;
    }

    @Override
    public void run() {
        synchronized (warehouse) {
            while (true) {
                // 1.判断仓库有没有手机
                if (warehouse.isEmpty()) {
                    // 3.如果没有手机,wait
                    try {
                        warehouse.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    // 2.如果有手机,消费掉,然后notify
                    warehouse.sellPhone();
                    warehouse.notifyAll();
                }
            }
        }
    }
}

// 商品类型
class Phone {
    private String name;
    private double price;

    public Phone(String name, double price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public String toString() {
        return "Phone{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

// 仓库类型,用于存储商品
class Warehouse {
    // 用于被消费者和生产者进行操作的对象(生产者相当于给它赋值对象,消费者相当于给它赋值null)
    private Phone phone;

    // 提供两个方法,用于生产者生产手机,消费者消费手机
    public void pullPhone(Phone phone) {
        this.phone = phone;
        System.out.println(Thread.currentThread().getName() + "生产了一部手机:" + phone);
    }

    public void sellPhone() {
        Phone sellPhone = phone;
        phone = null;
        System.out.println(Thread.currentThread().getName() + "消费了一部手机:" + sellPhone);
    }

    // 提供一个判断仓库有无手机的方法,true表示没有手机,false表示有手机
    public boolean isEmpty() {
        return phone == null;
    }

}